package com.zoro.barn.commons.webcommons.feign;

import com.fasterxml.jackson.databind.JsonNode;
import com.zoro.barn.commons.foundation.web.FeignResponseHandleException;
import com.zoro.barn.commons.foundation.web.FeignResponseHandler;
import com.zoro.barn.commons.tools.json.JsonUtil;
import com.zoro.barn.commons.webcommons.response.advice.ResponseDataAdvice;
import com.zoro.barn.commons.webcommons.response.entity.BarnResponseEntity;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import org.apache.commons.collections4.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;


/**
 * 当使用Feign 调用一个服务时 ，因为下游服务可能会经过 {@link  ResponseDataAdvice } 包装，
 * 可以通过这个 decoder 进行自动解包,仅支持对具体类型声明的自动解包，不支持参数化类型的解包
 * <pre>
 *     <T> T method();
 * <pre/>
 * @author zhaoxingwu
 */
public class BarnResponseEntityDecoder implements Decoder {

    /**
     * 用于对响应值做更进一步的处理
     */
    private final List<FeignResponseHandler> responseHandlers = new ArrayList<>();

    public BarnResponseEntityDecoder() {
    }

    public void addResponseHandler(FeignResponseHandler handler) {
        this.responseHandlers.add(handler);
    }

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        InputStream is = response.body().asInputStream();
        JsonNode responseJson = JsonUtil.readJson(is);
        Object decodeResult = doDecode(response, type, responseJson.toString());
        return handle(response, type, decodeResult, responseJson.toString());
    }

    private Object handle(Response response, Type type, Object data, String responseJson) throws FeignException {
        if (CollectionUtils.isEmpty(this.responseHandlers)) {
            return data;
        }
        Object result = data;
        for (FeignResponseHandler handler : this.responseHandlers) {
            try {
                result = handler.handle(type, result, responseJson);
            } catch (FeignResponseHandleException e) {
                throw new DecodeException(response.status(),
                        "error occurred when " + handler.getClass().getName() + " was invoked.",
                        response.request(), e);
            }
        }
        return result;
    }

    /**
     * @param response     调用服务接口的响应对象
     * @param type         feign 接口定义的返回值类型
     * @param responseJson response body 的 json 字符串，暂时不考虑其他形式的返回格式， 将这个字符串也传入的原因是 response 的 inputStream 是不可以反复读取的
     * @return 解析后的数据
     * @throws IOException    e
     * @throws FeignException e
     */
    private Object doDecode(Response response, Type type, String responseJson) throws IOException, FeignException {
        if (type instanceof Class<?> clazz) {
            if (BarnResponseEntity.class.isAssignableFrom(clazz)) {
                // BarnResponseEntity methodName(); 这种情况不需要额外处理
                return JsonUtil.toJavaType(responseJson, BarnResponseEntity.class);
            } else {
                // User methodName();这种情况需要 将响应数据转为 BarnResponseEntity, 再将 data 返回
                BarnResponseEntity<?> entity = JsonUtil.toJavaType(responseJson,
                        new BarnResponseEntityParameterizedType(getCanInitialiseClass(clazz)));
                return entity == null ? null : entity.getData();
            }
        } else if (type instanceof ParameterizedType pt) {
            Type rawType = pt.getRawType();
            if (rawType instanceof Class<?> clazz) {
                // BarnResponseEntity<User> methodName(); 这种情况不需要额外处理
                if (BarnResponseEntity.class.isAssignableFrom(clazz)) {
                    return JsonUtil.toJavaType(responseJson, type);
                } else {
                    // List<User> methodName();
                    BarnResponseEntity<?> entity = JsonUtil.toJavaType(responseJson,
                            new BarnResponseEntityParameterizedType(getCanInitialiseClass(clazz)));
                    return entity == null ? null : entity.getData();
                }

            } else {
                throw new DecodeException(response.status(),
                        "type is not an instance of Class or ParameterizedType: " + type,
                        response.request());
            }
        } else {
            throw new DecodeException(response.status(),
                    "type is not an instance of Class or ParameterizedType: " + type,
                    response.request());
        }
    }

    /**
     * 通过Class 查找一个可以被被初始化的类
     * @param clazz class
     * @return class
     */
    private Class<?> getCanInitialiseClass(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }
        if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
            // 是接口或者抽象类
            if (Collection.class.isAssignableFrom(clazz)) {
                return ArrayList.class;
            }
            if (Map.class.isAssignableFrom(clazz)) {
                return HashMap.class;
            }
            throw new RuntimeException("[ " + clazz.getName() + " ] is a Interface or abstract class");
        }
        return clazz;
    }


    private static class BarnResponseEntityParameterizedType implements ParameterizedType {

        private final Type[] type;

        private BarnResponseEntityParameterizedType(Type type) {
            this.type = new Type[]{type};
        }

        @Override
        public Type[] getActualTypeArguments() {
            return type;
        }

        @Override
        public Type getRawType() {
            return BarnResponseEntity.class;
        }

        @Override
        public Type getOwnerType() {
            // 不支持内部类解析
            return null;
        }
    }
}
